<template>
  <div ref="playerDom" class="music-player">
    <div class="ctrl-area">
      <NButton v-if="featuresCode === 'player'" :disabled="!musicSmooth" @click="handleClickBtn"
        >载入音频</NButton
      >
      <NButton v-if="featuresCode === 'autoplay'" :disabled="!musicSmooth" @click="handleClickBtn"
        >切歌</NButton
      >
      <NButton v-if="featuresCode === 'joke'" :disabled="!musicSmooth" @click="handleClickBtn"
        >切歌</NButton
      >
      <NButton
        v-if="['setting', 'play'].includes(featuresCode)"
        :disabled="!musicSmooth"
        @click="handleClickBtn"
        >{{ searchValue ? '配置' : '切歌' }}</NButton
      >
    </div>
    <div ref="bgDom" id="bg"></div>
    <div id="lyric">
      <div class="lrc-text1">
        <div class="lrc-text-all" :content="currentLrcOrLoading">
          {{ currentLrcOrLoading }}
        </div>
      </div>
    </div>
  </div>
  <!-- 图片缓存 -->
  <img ref="imgTemp" class="img_temp" src="" alt="" />
  <Toast ref="commonToast"></Toast>
</template>
<script setup lang="ts">
import Toast from '@/components/toast.vue';
import { ref, Ref, computed, nextTick } from 'vue';
import { NButton } from 'naive-ui';
import freshThreeAudio from './ThreeTotal';
const commonToast = ref(null);
// 图片api接口
const imgsApis = [
  // 'https://static-mp-1c925fd0-d9e0-409d-b254-d061358b31f9.next.bspapp.com/assets/left.jpg',
  'https://cdn.seovx.com/?mom=302', // pc
  'https://t.mwm.moe/fj/', // pc
  'https://api.gumengya.com/Api/MvImg?format=image', // pc
  'https://tuapi.eees.cc/api.php?category=meinv&type=302', // pc
  'https://cdn.seovx.com/ha/?mom=302', // pc
  'http://api.btstu.cn/sjbz/?lx=suiji', // pc
  'http://api.yujn.cn/api/yht.php?type=image',
  'https://api.vvhan.com/api/wallpaper/pcGirl', // pc
  'http://api.btstu.cn/sjbz/', // pc
  'https://api.zylearning.top/api/wallpaper?lx=mnmt', //pc
  'http://dsyai.club/api/youhuotu.php', // mobile + pc
  'https://www.onexiaolaji.cn/RandomPicture/api/?key=qq249663924', // mobile + pc
  'https://api.yujn.cn/api/meinv.php',
  'http://api.yujn.cn/api/bizhi.php?msg=1', // mobile
  'https://api.vvhan.com/api/wallpaper/mobileGirl', // mobile
  'http://api.yujn.cn/api/bizhi.php?msg=7', // mobile
  'http://api.yujn.cn/api/xiezhen.php?type=image', // mobile
  'https://api.lolimi.cn/API/tup/xjj.php', // mobile
  'https://api.asxe.vip/whitesilk.php', // mobile
  'https://v2.api-m.com/api/meinvpic?return=302', // mobile
  'https://v2.api-m.com/api/baisi?return=302', // mobile
  'https://v2.api-m.com/api/heisi?return=302', // mobile
  'https://api.suyanw.cn/api/ksxjj', // mobile
];

// preload方法的后置守卫
function handleResponse(response) {
  if (response.code === -1) {
    console.log('Debugger ~ handleResponse ~ response:', response);
    $message(response.errMsg);
    return false;
  }
  return true;
}

// 判断是否合法
function isCheckDev(safeTime) {
  if (
    new Date().getTime() > new Date(safeTime).getTime() ||
    localStorage.getItem('villiam') + '' === '1'
  ) {
    return true;
  }
  return false;
}

const DEV_TEST = !import.meta.env.PROD;

// 绕过审核时间 生产环境需要去校验
const IsSafe = DEV_TEST || isCheckDev('2024-05-01');

// utools.dbStorage的key枚举
enum DBKeyEnum {
  'MusicListId' = 'MUSICLISTID',
  'MusicLocalPath' = 'MUSICLOCALPATH',
  'LrcLocalPath' = 'LRCLOCALPATH',
}

// 随机返回数组的合法索引
function randomArrayIndex(arr) {
  return (Math.random() * arr.length) | 0;
}

// 默认歌单id
let musicListId = '367231494';
// 随机返回歌单id的一首歌api
const musicApi = 'https://api.52vmy.cn/api/music/wy/rand?id=';

// LRC的api
const lrcApi = 'https://api.linhun.vip/api/wyyyy?apiKey=070668bc7e5965190df2486c3b5eea84';

// 脱口秀节目单的索引
let jokeSourceIndex: Ref<number> = ref(-1);

// 脱口秀节目单列表
const jokeSource = [
  '糗事播报',
  '段子来了',
  '波波有理',
  '妙宇连朱',
  '非常溜佳期',
  '今晚脱口秀',
  '娱乐逗翻天',
  '上班脱口秀',
];

// 消息提示封装
function $message(msg) {
  commonToast.value.showToast(typeof msg === 'object' ? JSON.stringify(msg) : msg);
  if (utools) {
    utools.showNotification(typeof msg === 'object' ? JSON.stringify(msg) : msg);
  }
}

// 拼接路由和参数
function jsonQuery(url, query = {}) {
  const paramsArr = [];
  for (let [key, value] of Object.entries(query)) {
    if (key && value) {
      paramsArr.push(key + '=' + value);
    }
  }
  const params = paramsArr.join('&');
  return params ? [url, params].join(url.includes('?') ? '&' : '?') : url;
}

// 加时间参数
function addSalt(url) {
  const salt = 'time=' + new Date().getTime();
  return [url, salt].join(url.includes('?') ? '&' : '?');
}

// 配置项
// - 背景是否静态
// - 随机背景的来源
// - 背景图片的来源, 当isRandomApi = false 生效
// - 背景图片切换的间隔, 单位 秒
const config = {
  staticBg: DEV_TEST,
  isRandomApi: !DEV_TEST,
  imgsApisIndex: 0,
  bgToggleTime: 10,
};

// 背景的缓存
const imgTemp = ref(null);

// 背景的元素
const bgDom = ref(null);

// 背景切换的setTimeout id
let bgTimeId = null;

// 切换背景的实现
function transformBg() {
  if (config.isRandomApi) {
    config.imgsApisIndex = Math.floor(Math.random() * imgsApis.length);
  }
  let imgSrcTemp = '';
  imgSrcTemp = addSalt(imgsApis[config.imgsApisIndex]);
  nextTick(() => {
    if (imgTemp.value) {
      imgTemp.value.src = imgSrcTemp;
      imgTemp.value.onload = function () {
        if (bgDom.value && imgTemp.value) {
          bgDom.value.style.backgroundImage = `url("${imgTemp.value.src}")`;
        }
        restartTransformBg();
      };

      imgTemp.value.onerror = function () {
        restartTransformBg();
      };
    }
    restartTransformBg();
  });
}

// 定时切换背景的实现
function restartTransformBg() {
  clearTimeout(bgTimeId);
  bgTimeId = setTimeout(async () => {
    transformBg();
  }, config.bgToggleTime * 1000);
}

// preload返参的接口协议
interface IResponse {
  code: number;
  data: string;
  errMsg: string;
}

// 路径类型的接口协议
type TGetFileRequest = (path: string) => Promise<IResponse>;

// utoolsPreload的协议
interface IUtoolsPreload {
  getMediaFile: TGetFileRequest;
  getLrcFile: TGetFileRequest;
  getMediaDir: TGetFileRequest;
  validDir: TGetFileRequest;
}

// 自定义window的挂载
declare var window: {
  utools: any;
  utoolsPreload: IUtoolsPreload;
  threeAudio: any;
  bridge: any;
  dolly: any;
  soundwave: any;
  app: any;
  addEventListener: Function;
  onerror?: Function;
};

let { utoolsPreload, utools } = window;

// 输入框的响应式变量
const searchValue = ref('');

// 进入插件的code场景, 跟plugin.json中的features.code对应
const featuresCode = ref('');
// 启动插件输入框的内容
const payloadInput = ref('');

// 是否在进入组件时候初始化
let isInit = false;

// 进入插件
utools && utools.onPluginEnter(handlePluginEnter);

// MOCK场景 9874598026 367231494
const MOCKENTERCONFIG = {
  code: 'autoplay',
  type: 'regex',
  payload: `分享好好睡觉好好睡一觉创建的歌单「vip」: https://y.music.163.com/m/playlist?id=367231494&userid=268561011&creatorId=268561011 (来自@网易云音乐)`,
  option: undefined,
};

// MOCK场景
setTimeout(() => {
  // 生产环境不走这里
  DEV_TEST && true && handlePluginEnter(MOCKENTERCONFIG);
}, 1500);

// 进入H5逻辑
function handleH5Init() {
  featuresCode.value = 'autoplay';
  sceneFn(featuresCode.value);
}

// 处理插件进入逻辑
async function handlePluginEnter({ code, type, payload, option }) {
  if (!window.utools) {
    handleH5Init();
    return;
  }

  if (isInit) return;
  console.log('用户进入插件应用', code, type, payload, option);
  isInit = true;
  const redirectSceneCode = ['autoplay', 'joke'];
  featuresCode.value = IsSafe || !redirectSceneCode.includes(code) ? code : 'setting';
  payloadInput.value = payload;

  // 输入框的初始化
  searchInputFactory(featuresCode.value);
  if (type === 'regex' && (IsSafe || !redirectSceneCode.includes(code))) {
    regexSceneFn(featuresCode.value);
  } else {
    sceneFn(featuresCode.value);
  }
}

// 点击按钮的逻辑处理
async function handleClickBtn() {
  if (!musicSmooth.value) return;
  // 点击配置时候不能清空输入框
  if (['play', 'setting'].includes(featuresCode.value) && searchValue.value) {
    const isValid = await settingLocalPath(searchValue.value);
    if (!isValid) {
      $message('请输入正确的目录');
      return;
    }
  }
  // 重置播放状态
  resetPlay(false);
  switch (featuresCode.value) {
    case 'player':
      onSelectMediaFile();
      break;
    case 'autoplay':
      getMusicInfoFromNet();
      break;
    case 'joke':
      getJokeUrl();
      break;
    case 'play':
    case 'setting':
      playLocalMusic();
      break;
  }
}

// 设置本地目录 带校验
async function settingLocalPath(pathStr, pathType = DBKeyEnum.MusicLocalPath) {
  if (pathStr) {
    const pathTransform = pathStr.replace(/\\/g, '/').replace(/[\"\'']/g, '');
    const res = await window.utoolsPreload.validDir(pathTransform);
    if (res.code === 1) {
      utools.dbStorage.setItem(pathType, pathTransform);
      return true;
    }
  }
  $message('请输入正确的目录');
  return false;
}

// 正则下的场景处理
async function regexSceneFn(code) {
  switch (code) {
    case 'player':
      searchValue.value = payloadInput.value;
      onClickGenerateBtn();
      break;
    case 'autoplay': {
      if (/^\d*$/i.test(payloadInput.value)) {
        utools.dbStorage.setItem(DBKeyEnum.MusicListId, payloadInput.value);
      } else {
        utools.dbStorage.setItem(
          DBKeyEnum.MusicListId,
          payloadInput.value.match(/[\?\&]?id=(\d+)/)[1],
        );
      }
      getMusicInfoFromNet();
      break;
    }
    case 'joke':
      jokeSourceIndex.value = jokeSource.findIndex((item) => item.includes(payloadInput.value));
      getJokeUrl();
      break;
    case 'setting': {
      await settingLocalPath(payloadInput.value);
      utools.dbStorage.getItem(DBKeyEnum.MusicLocalPath) && playLocalMusic();
      break;
    }
    case 'settingLrc': {
      const valid = await settingLocalPath(payloadInput.value, DBKeyEnum.LrcLocalPath);
      if (valid) {
        $message('配置成功, 请将同名且UTF8编码的歌词文件(lrc,txt)放到该文件夹下');
        utools.shellShowItemInFolder(payloadInput.value);
        utools.hideMainWindow();
      }
      break;
    }
  }
}

// 普通进入插件的场景处理
function sceneFn(codeParams) {
  const codeComputed = IsSafe ? codeParams : 'setting';
  setTimeout(() => {
    switch (codeComputed) {
      case 'player':
        $message('请载入一个音频文件');
        break;
      case 'autoplay':
        getMusicInfoFromNet();
        break;
      case 'joke':
        getJokeUrl();
        break;
      case 'play':
      case 'setting':
        playLocalMusic();
        break;
    }
  }, 1500);
}

// 输入框提示 输入框的初始化
function searchInputFactory(code) {
  switch (code) {
    case 'play':
      utools.setSubInput(({ text }) => {
        searchValue.value = text;
      }, '本地音乐文件所在目录, 例子:C:\\Users\\Admin\\Music');
      break;
    case 'player':
      utools.setSubInput(({ text }) => {
        searchValue.value = text;
      }, '音频文件,例子:C:\\Users\\Admin\\Music\\musicFile.mp3');
      break;
    case 'setting':
      utools.setSubInput(({ text }) => {
        searchValue.value = text;
      }, '本地音乐文件所在目录, 例子:C:\\Users\\Admin\\Music');
      break;
  }
}

config.staticBg || restartTransformBg();

window.addEventListener('resize', () => {
  window.app && window.app.setup && window.app.setup();
});

let audio = undefined;
let animalMediaTimeId = undefined;

// 音频播放的回调函数
function canplayFn() {
  console.log('可以播放了');
  audio.isPlaying = true;
  animalMedia();
}

// 用来标志是否没有受到人为中断, 顺利播放
let musicSmooth = ref(true);

function handleThreeAudioLoaded(init): Function {
  console.log('音乐初始化', init);
  musicSmooth.value = true;
  audio = window.threeAudio.analyser.context;
  canplayFn();
  return (newStatus) => {
    if (!newStatus && musicSmooth.value) {
      musicSmooth.value = false;
      // 如果音乐自动播放到结束走这里的逻辑
      console.log('音乐播放到结束了');
      sceneFn(featuresCode.value);
    }
  };
}

// 从网易歌单随机获取音乐路径
function getMusicInfoFromNet() {
  const listId = (utools && utools.dbStorage.getItem(DBKeyEnum.MusicListId)) || musicListId;
  fetch(musicApi + listId)
    .then((response) => response.json())
    .then(async (data) => {
      const { singer, song, Music, id } = data.data;
      if (!Music) {
        $message('请求失败,请重试');
        musicSmooth.value = true
        return;
      }
      lineLrcList.value = [
        {
          time: 0,
          text: song + '-' + singer,
        },
      ];
      curIndex.value = 0;
      getThreeAudioInstance(Music)
        .then(() => {
          getLrcFromNet(id);
        })
        .catch(() => {
          // 自动下一首
          setTimeout(() => {
            getMusicInfoFromNet();
          }, 3000);
        });
    });
}

const jokeApiKey = '0dc58c03fc417b744a59aa5393db3dfa';
// 随机获取脱口秀路径
function getJokeUrl() {
  const jokeSourceName =
    jokeSource[jokeSourceIndex.value] || jokeSource[(Math.random() * jokeSource.length) | 0];
  const jokeApi = jsonQuery('https://api.linhun.vip/api/xmly', {
    lx: jokeSourceName,
    type: 'json',
    apiKey: jokeApiKey,
  });
  fetch(jokeApi)
    .then((response) => response.json())
    .then((data) => {
      const { tag: author, musicUrl: mp3url, title: name } = data.meta.music;
      if (!mp3url) {
        $message('请求失败,请重试');
        musicSmooth.value = true
        return;
      }
      lineLrcList.value = [
        {
          time: 0,
          text: name + '-' + author,
        },
      ];
      curIndex.value = 0;
      getThreeAudioInstance(mp3url);
    });
}

function getLrcFromNet(musicId) {
  fetch(lrcApi + '&id=' + musicId)
    .then((response) => response.json())
    .then((data) => {
      const { lyric, author, name } = data;
      if (lyric.length !== 0) {
        lineLrcList.value = [
          {
            time: 0,
            text: name + '-' + author,
          },
        ].concat(
          data.lyric.map((item) => {
            return {
              time: transformTimeFormat(item.time),
              text: item.name,
            };
          }),
        );
        animalMedia();
      }
    });
}

// 将本地音乐文件生成音频实例
async function createAudio(path = '/Users/mac/Desktop/V_mp3/陈壹千-一个.mp3') {
  const res = await utoolsPreload.getMediaFile(path);
  return new Promise((resolve, reject) => {
    if (handleResponse(res)) {
      getThreeAudioInstance(res.data)
        .then(() => {
          resolve(true);
        })
        .catch(() => {
          reject(false);
        });
    } else {
      reject(false);
    }
  });
}

// 链接正常 返回 Promise.resolve(true)
// 链接错误 返回 Promise.reject(false)
function getThreeAudioInstance(musicUrl) {
  return new Promise((resolve, reject) => {
    audio && (audio.isPlaying = false);
    freshThreeAudio(musicUrl, document.querySelector('.music-player'), handleThreeAudioLoaded)
      .then(() => {
        resolve(true);
      })
      .catch(() => {
        $message('链接不正确等原因导致音频加载错误, 如果功能受影响请重启插件');
        reject(false);
      });
  });
}
// 更新歌词
function animalMedia() {
  if (lineLrcList.value.length === 0 || !audio) return;
  // 如果音频还没开始, 先显示第0秒
  const threeAudioCurrentTime = audio.isPlaying
    ? audio.currentTime - window.threeAudio.sound.startTime
    : 0;
  curIndex.value = (lineLrcList.value as any).findLastIndex((item) => {
    return threeAudioCurrentTime >= item.time;
  });
  const nexLrc: ILrc = lineLrcList.value[curIndex.value + 1];
  clearTimeout(animalMediaTimeId);
  if (nexLrc) {
    const calcTime = ((nexLrc.time - threeAudioCurrentTime) * 1).toFixed(4);
    animalMediaTimeId = setTimeout(() => {
      animalMedia();
    }, Math.min(Math.max(parseFloat(calcTime), 0.1), 3) * 1000);
  } else {
    animalMediaTimeId = setTimeout(() => {
      animalMedia();
    }, 3 * 1000);
  }
}

function playLocalMusic() {
  const path = utools.dbStorage.getItem(DBKeyEnum.MusicLocalPath);
  if (path) {
    //  将 window 的路径转为统一的 linux的
    const pathTransform = path.replace(/\\/g, '/');
    window.utoolsPreload.getMediaDir(pathTransform).then((res) => {
      if (handleResponse(res)) {
        const musicList = res.data;
        if (musicList && musicList.length === 0) {
          $message('该目录下不存在音乐文件,请重新配置目录');
          musicSmooth.value = true;
          return;
        }
        const musicFilePath = [pathTransform, musicList[randomArrayIndex(musicList)]].join('/');
        onClickGenerateBtn(musicFilePath);
      }
    });
  } else {
    musicSmooth.value = true;
    $message('请先配置本地音乐目录');
  }
}

// 如果输入框有内容, 则将输入框内容识别为媒体文件位置
// 没有内容, 让用户选择音乐文件
function onSelectMediaFile() {
  if (searchValue.value) {
    onClickGenerateBtn();
  } else {
    const selectedFile = utools.showOpenDialog({
      filters: [{ name: 'mp3', extensions: ['mp3'] }],
      properties: ['openFile'],
    });
    if (selectedFile) {
      searchValue.value = selectedFile[0];
      onClickGenerateBtn();
    }
  }
}

interface ILrc {
  time: number;
  text: string;
}

const curIndex: Ref<number> = ref(0);
const lineLrcList: Ref<Array<ILrc>> = ref([]);

const currentLrc = computed(() => {
  return lineLrcList.value[curIndex.value]?.text;
});

const currentLrcOrLoading = computed(() => {
  return featuresCode.value ? currentLrc.value : 'Loading';
});

// 通知显示过一次就不显示了
let hadShowTips = false;
function getLocalLrc(path = '/Users/mac/Desktop/V_mp3/陈壹千-一个.lrc') {
  const lrcSettingPath = utools.dbStorage.getItem(DBKeyEnum.LrcLocalPath);
  let settingPathLrcDir = path;
  if (lrcSettingPath) {
    settingPathLrcDir = [lrcSettingPath, path.match(/\/([^\/]+)\s*$/)[1]].join('/');
  }
  utoolsPreload.getLrcFile(settingPathLrcDir).then((res) => {
    if (res.code === 1) {
      analysisLrc(res.data);
    } else {
      lineLrcList.value = [
        {
          time: 0,
          text:
            settingPathLrcDir.replace(/\.(lrc|txt)$/, '').match(/\/([^\/]+)\s*$/)[1] ||
            settingPathLrcDir,
        },
      ];
      if (!hadShowTips && res.errMsg.includes('音乐文件同名')) {
        $message(res.errMsg);
        hadShowTips = true;
      } else if (!res.errMsg.includes('音乐文件同名')) {
        $message(res.errMsg);
      }
    }
  });
}

// 解析LRC文件
function analysisLrc(lrcContent: string) {
  lineLrcList.value = lrcContent
    .split('\n')
    .filter((item: string) => {
      return /^\[\d+\:\d+\.\d+\]/.test(item.trim());
    })
    .map((item: string): ILrc => {
      return {
        time: transformTimeFormat(item.match(/\d+\:\d+\.\d+/)[0]),
        text: item.split(']')[1].trim(),
      };
    });
}

// 将时间文本转为数字
function transformTimeFormat(timeStr: string = '00:00.000'): number {
  const [minute, secondes] = timeStr.split(':');
  return Number(minute) * 60 + parseFloat(secondes);
}

// 加载本地mp3,加载歌词文件,重置播放
async function onClickGenerateBtn(localMusicPath = searchValue.value) {
  if (localMusicPath) {
    // 将window路径的 \ 统一转为 /; 存在引号都删掉
    const pathTransform = localMusicPath.replace(/\\/g, '/').replace(/[\"\'']/g, '');
    createAudio(pathTransform)
      .then((audioValid) => {
        // 获取本地的音乐文件地址
        audioValid && getLocalLrc(pathTransform.replace('.mp3', '.lrc'));
        resetPlay();
      })
      .catch(() => {
        musicSmooth.value = true;
      });
  }
}

// 重置播放 - 手动中断播放,重置歌词,清空输入框
function resetPlay(clearSearchValue = true) {
  musicSmooth.value = false;
  lineLrcList.value = [];
  utools && utools.setSubInputValue('');
  clearSearchValue && (searchValue.value = '');
}
</script>

<style scoped>
@font-face {
  font-family: MO;
  src: url(https://static-mp-1c925fd0-d9e0-409d-b254-d061358b31f9.next.bspapp.com/musicPlay/assets/font-type.ttf);
}
.music-player {
  font-family: MO;
  position: relative;
}

.ctrl-area {
  position: fixed;
  right: 10px;
  top: 10px;
  z-index: 11;
}

#bg {
  background: url('https://cdn.seovx.com/?mom=302') center no-repeat; /*图片接口挂了*/
  /* background-image: url("https://api.311i.cn/api/sjbz.php?method=pc&lx=fengjing"); */
  background-size: 120%;
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: -1;
  filter: blur(1px);
  animation: moveBg 60s ease infinite both;
}

@keyframes moveBg {
  0% {
    background-position: 50% 50%;
  }
  10% {
    background-position: 60% 60%;
  }
  20% {
    background-position: 70% 50%;
  }
  30% {
    background-position: 60% 40%;
  }
  40% {
    background-position: 50% 30%;
  }
  50% {
    background-position: 60% 40%;
  }
  60% {
    background-position: 50% 50%;
  }
  70% {
    background-position: 40% 60%;
  }
  80% {
    background-position: 30% 70%;
  }
  90% {
    background-position: 40% 60%;
  }
  100% {
    background-position: 50% 50%;
  }
}

#bg:after {
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  content: '';
  background-color: rgba(0, 0, 0, 0.3);
}

.img_temp {
  display: inline;
  width: 0;
  height: 0;
  position: fixed;
  left: 0;
  top: 0;
  z-index: -99999;
}

#lyric {
  text-align: center;
  color: #eee;
  font-family: 'MO';
  font-size: 3vw;
  text-shadow: 1px 1px 10px #111;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100vh;
  position: relative;
  z-index: 7;
}

.lrc-text {
  margin: 70px 0;
  animation: show 2s backwards;
  width: 100%;
  position: relative;
}
.lrc-text-all {
  position: relative;
  animation: show 2s backwards;
  margin: 50px 0;
}
/* 
#lyric .lrc-text:not(:last-child) {
  transform: scale(var(--scaleNum)) translateX(-10%);
}
#lyric .lrc-text:last-child {
  transform: scale(var(--scaleNum)) translateX(10%);
} */
#lyric .lrc-text::before,
#lyric .lrc-text-all::before {
  color: #aaa;
  content: attr(content);
  position: absolute;
  inset: 0;
  z-index: -1;
  -webkit-mask: linear-gradient(
    0deg,
    rgba(255, 255, 255, 0.4) 0%,
    rgba(255, 255, 255, 0.4) 20%,
    rgba(255, 255, 255, 0.2) 30%,
    transparent 50%,
    transparent 100%
  );
  transform: rotatex(180deg) translatey(11px) skew(145deg) translatex(-5px);
  transform-origin: 50% 100%;
}
</style>

<style>
#app {
  max-height: 100vh;
  max-width: 100vw;
  overflow: hidden;
}
#three_canvas {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 5;
}
</style>
